package hk.config;
import javax.sql.DataSource;
import org.springframework.context.annotation.*;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.*;
import org.springframework.security.config.annotation.web.configurers.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.*;
import org.springframework.web.cors.CorsConfigurationSource;
import hk.security.*;
import hk.service.impl.UserDetailsServiceImpl;
import jakarta.annotation.Resource;

//开启方法级别的权限检查,如果要使用注解进行详细的权限配置,要加这个注解
@EnableMethodSecurity(securedEnabled = true) 
@Configuration
@EnableWebSecurity
public class SecurityConfig {	
	@Resource
	UserDetailsServiceImpl myUserDetailsServiceImpl;  //用于rememberMe
	
	@Resource
	DataSource dataSource; //用于rememberMe
	
	@Bean
	public BCryptPasswordEncoder getPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	public  PersistentTokenRepository tokenRepo(DataSource dataSource) { //用于rememberMe
		JdbcTokenRepositoryImpl repo=new JdbcTokenRepositoryImpl();
		repo.setDataSource(dataSource);
	//	repo.setCreateTableOnStartup(true); //只能第一次时用,建完表后,再把它注掉
		return repo;
	}
	
	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http, CorsConfigurationSource configurationSource)
			throws Exception {
		 //=========================================================================================================
		 Customizer<FormLoginConfigurer<HttpSecurity>> custom =conf -> {
			 conf.loginPage("/login") //客户端发这个请求,它会帮我们转到对应的视图,就是那个登录界面, 需要在控制层中配置 /login这个请求,必须是get
	            .loginProcessingUrl("/do-login")  //这个是用来处理登录请求的url, 必须是post
                .usernameParameter("name")
                .passwordParameter("password")
             //  .defaultSuccessUrl("/home")
                 .successForwardUrl("/home")
             //  .successHandler(new AuthOkHandler())
                .failureUrl("/login?error")
                .failureForwardUrl("/login?error")
                .failureHandler(new AuthFailureHandler()) ;
	     };
		 http.formLogin(custom);
		 //==========================================================================================================
		 
		 
		//=========================================================================================================
		 Customizer<RememberMeConfigurer<HttpSecurity>> rememberMeCustomizer=conf->{
			 conf.tokenRepository(tokenRepo(dataSource))
			 .rememberMeParameter("remember-me")
			 .rememberMeCookieName("HK_REMEMBER_ME")
		//	 .rememberMeCookieDomain("localhost")  默认会自动解析
			 .tokenValiditySeconds(18000)   //默认值是1800秒  30分钟
			 .userDetailsService(myUserDetailsServiceImpl);   //我们用来实现那个UserDetailService的实现类	 
		 }; 
		 http.rememberMe(rememberMeCustomizer);
		//=========================================================================================================
		 
		 
		//=========================================================================================================
		 Customizer<LogoutConfigurer<HttpSecurity>> logoutCustomizer=conf->{
			 conf.logoutUrl("/logout")  //默认就是logout
			 .logoutSuccessUrl("/login");  //退出成功后的跳转地址,默认是 登录页面地址?logout
		  // .logoutSuccessHandler(new LogoutOkHandler())  //退出登录成功后的处理
		  // .addLogoutHandler(new MyLogoutHandler());   //额外的退出登录成功的代码
		 };
		 http.logout(logoutCustomizer);
		//=========================================================================================================
		 
		 
		//=========================================================================================================
		 Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> reqCustomizer=auth->{
			 auth.requestMatchers("/login","do-login","loginFail").permitAll()
		//	 .requestMatchers("/js/**","/images/**").hasRole("ADMIN")
		//	 .requestMatchers("/js/**","/images/**").hasAnyRole("ADMIN","USER")
		//	 .requestMatchers("/js/**","/images/**").hasAuthority("js:list")
			 .requestMatchers("/js/**","/images/**").hasAnyAuthority("js:listxxx","img:listxxx")
		//	 .requestMatchers(HttpMethod.GET,"/js/**","/images/**").permitAll() 也可以这样只控制某种请求才行
			 .anyRequest().authenticated();   //其他所有的地址,必须写在最后
		 };
		 http.authorizeHttpRequests(reqCustomizer);
		//=========================================================================================================
		 
		 
		//=========================================================================================================
		 Customizer<ExceptionHandlingConfigurer<HttpSecurity>> exceptCustomizer=conf->{
			 conf.accessDeniedHandler(new  AccDeniedHandler());
			// .authenticationEntryPoint(xxx) 登录失败的处理,一般很少使用
		 };
		 http.exceptionHandling(exceptCustomizer);
		//=========================================================================================================

		 
		//=========================================================================================================
		//禁用跨站请求伪造 spring security 会帮我们把请求转到它的默认登录页,并添一个:<input name="_csrf" type="hidden" value="cTdF...BF" 这样隐藏域
		//如果我们不需要,可以以禁掉
		//http.csrf((t)->t.disable()); 
		//=========================================================================================================
        
		 return http.build();
	}
}